<?php

class func
{
    static function url_query_decode($string)
    {
        return parse_str($string);
    }

    static private function DOT($a, $b)
    {
        $result = '';
        foreach (func_get_args() as $s) {
            $result .= $s;
        }
        return $result;
    }

    static function url_query_encode($hash)
    {
        $map = array();
        self::walk_recursive_with_ancestors(
            $hash,
            array(__CLASS__, 'url_query_encode_callback'),
            array(&$map)
        );

        return implode('&', array_map(array(__CLASS__, 'DOT'), array_keys($map), array_fill(0, count($map), '='), $map));
    }

    static private function url_query_encode_callback($item, $key, $ancestors, $map)
    {
        array_push($ancestors, $key);
        $ancestors = array_map('urlencode', $ancestors);
        $key = array_shift($ancestors);
        if (! empty($ancestors)) {
            $key .= '['.implode('][', $ancestors).']';
        }
        if (array_key_exists($key, $map[0])) {
            throw new Exception('Duplicate key');
        }
        $map[0][$key] = urlencode(strval($item));
    }

    /**
     * Аналогична array_walk_recursive(), только в $callback передаёться третьим параметром
     * массив ключей предков.
     *
     * @param array &$array
     * @param callback $callback function(mixed &$item, mixed $key, array $ancestors [, mixed $userdata])
     * @param [optional]mixed $userdata
     *
     * @return bool Возвращает TRUE в случае успешного завершения или FALSE в случае возникновения ошибки.
     */
    static function walk_recursive_with_ancestors(&$array, $callback, $userdata = null)
    {
        if (! is_array($array) || ! is_callable($callback)) {
            throw new Exception("array_walk_recursive_with_ancestors(): bad arguments");
        }

        reset($array);
        foreach ($array as $key => &$value) {
            if (is_array($value)) {
                self::_walk_recursive_with_ancestors($value, $callback, array($key), $userdata);
            } else {
                call_user_func_array($callback, array($value, $key, array(), $userdata));
            }
        }
    }

    static private function _walk_recursive_with_ancestors(&$array, $callback, $ancestors,  $userdata = null)
    {
        reset($array);
        foreach ($array as $key => &$value) {
            if (is_array($value)) {
                $ancestors[] = $key;
                self::_walk_recursive_with_ancestors($value, $callback, $ancestors, $userdata);
            } else {
                call_user_func_array($callback, array($value, $key, $ancestors, $userdata));
            }
        }
    }
}

?>